/* src/interfaces/ecpg/ecpglib/execute.c */

/*
 * 目标是获得一个更简单的数据库例程接口。
 * 所有关于元组的繁琐操作都应该被这个函数隐藏起来。
 */
/* 作者：Linus Tolke
   （实际上大部分代码是从发行版“借用”而来，仅仅
   略做修改）
 */

/* 于1998年2月5日由Michael Meskes <meskes@postgresql.org>
   作为PostgreSQL的一部分接管 */

#define POSTGRES_ECPG_INTERNAL
#include "postgres_fe.h"

#include <math.h>

#include "catalog/pg_type_d.h"
#include "ecpgerrno.h"
#include "ecpglib.h"
#include "ecpglib_extern.h"
#include "ecpgtype.h"
#include "pgtypes_date.h"
#include "pgtypes_interval.h"
#include "pgtypes_numeric.h"
#include "pgtypes_timestamp.h"
#include "sql3types.h"
#include "sqlca.h"
#include "sqlda-compat.h"
#include "sqlda-native.h"

/*
 * 这个函数返回一个新分配的字符串，已对'和\
 * 进行了转义。
 */
static char * fc_quote_postgres(char *fc_arg, bool fc_quote, int fc_lineno)
{
	char	   *fc_res;
	size_t		fc_length;
	size_t		fc_escaped_len;
	size_t		fc_buffer_len;

	/*
	 * 如果quote为假，我们只需要将内容存储在描述符中，
	 * 当它们被插入到语句中时会被引用。
	 */
	if (!fc_quote)
		return fc_arg;
	else
	{
		fc_length = strlen(fc_arg);
		fc_buffer_len = 2 * fc_length + 1;
		fc_res = (char *) ecpg_alloc(fc_buffer_len + 3, fc_lineno);
		if (!fc_res)
			return fc_res;
		fc_escaped_len = PQescapeString(fc_res + 1, fc_arg, fc_buffer_len);
		if (fc_length == fc_escaped_len)
		{
			fc_res[0] = fc_res[fc_escaped_len + 1] = '\'';
			fc_res[fc_escaped_len + 2] = '\0';
		}
		else
		{
			/*
			 * 我们不知道目标数据库是否使用
			 * standard_conforming_strings，因此我们始终使用E''字符串。
			 */
			memmove(fc_res + 2, fc_res + 1, fc_escaped_len);
			fc_res[0] = ESCAPE_STRING_SYNTAX;
			fc_res[1] = fc_res[fc_escaped_len + 2] = '\'';
			fc_res[fc_escaped_len + 3] = '\0';
		}
		ecpg_free(fc_arg);
		return fc_res;
	}
}

static void fc_free_variable(struct variable *fc_var)
{
	struct variable *fc_var_next;

	while (fc_var)
	{
		fc_var_next = fc_var->next;
		ecpg_free(fc_var);
		fc_var = fc_var_next;
	}
}

static void fc_free_statement(struct statement *fc_stmt)
{
	if (fc_stmt == NULL)
		return;
	fc_free_variable(fc_stmt->inlist);
	fc_free_variable(fc_stmt->outlist);
	ecpg_free(fc_stmt->command);
	ecpg_free(fc_stmt->name);
#ifndef HAVE_USELOCALE
	ecpg_free(fc_stmt->oldlocale);
#endif
	ecpg_free(fc_stmt);
}

static int fc_next_insert(char *fc_text, int fc_pos, bool fc_questionmarks, bool fc_std_strings)
{
	bool		fc_string = false;
	int			fc_p = fc_pos;

	for (; fc_text[fc_p] != '\0'; fc_p++)
	{
		if (fc_string && !fc_std_strings && fc_text[fc_p] == '\\')	/* 转义字符 */
			fc_p++;
		else if (fc_text[fc_p] == '\'')
			fc_string = fc_string ? false : true;
		else if (!fc_string)
		{
			if (fc_text[fc_p] == '$' && isdigit((unsigned char) fc_text[fc_p + 1]))
			{
				/* 这可以是美元引用或变量 */
				int			fc_i;

				for (fc_i = fc_p + 1; isdigit((unsigned char) fc_text[fc_i]); fc_i++)
					 /* 空循环体 */ ;
				if (!isalpha((unsigned char) fc_text[fc_i]) &&
					isascii((unsigned char) fc_text[fc_i]) && fc_text[fc_i] != '_')
					/* 不是美元限定的引用 */
					return fc_p;
			}
			else if (fc_questionmarks && fc_text[fc_p] == '?')
			{
				/* 也允许旧式占位符 */
				return fc_p;
			}
		}
	}

	return -1;
}

static bool fc_ecpg_type_infocache_push(struct ECPGtype_information_cache **fc_cache, int fc_oid, enum ARRAY_TYPE fc_isarray, int fc_lineno)
{
	struct ECPGtype_information_cache *fc_new_entry
	= (struct ECPGtype_information_cache *) ecpg_alloc(sizeof(struct ECPGtype_information_cache), fc_lineno);

	if (fc_new_entry == NULL)
		return false;

	fc_new_entry->oid = fc_oid;
	fc_new_entry->isarray = fc_isarray;
	fc_new_entry->next = *fc_cache;
	*fc_cache = fc_new_entry;
	return true;
}

static enum ARRAY_TYPE
fc_ecpg_is_type_an_array(int fc_type, const struct statement *fc_stmt, const struct variable *fc_var)
{
	char	   *fc_array_query;
	enum ARRAY_TYPE fc_isarray = ECPG_ARRAY_NOT_SET;
	PGresult   *fc_query;
	struct ECPGtype_information_cache *fc_cache_entry;

	if ((fc_stmt->connection->cache_head) == NULL)
	{
		/*
		 * 对于ecpg，文本类类型不是数组，但是postgres将它们
		 * 视为数组。这个定义提醒你不要“纠正”这些值。
		 */
#define not_an_array_in_ecpg ECPG_ARRAY_NONE

		/* 用众所周知的类型填充缓存以加快速度 */
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), BOOLOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), BYTEAOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), CHAROID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), NAMEOID, not_an_array_in_ecpg, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), INT8OID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), INT2OID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), INT2VECTOROID, ECPG_ARRAY_VECTOR, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), INT4OID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), REGPROCOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), TEXTOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), OIDOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), TIDOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), XIDOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), CIDOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), OIDVECTOROID, ECPG_ARRAY_VECTOR, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), POINTOID, ECPG_ARRAY_VECTOR, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), LSEGOID, ECPG_ARRAY_VECTOR, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), PATHOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), BOXOID, ECPG_ARRAY_VECTOR, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), POLYGONOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), LINEOID, ECPG_ARRAY_VECTOR, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), FLOAT4OID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), FLOAT8OID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), UNKNOWNOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), CIRCLEOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), MONEYOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), INETOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), CIDROID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), BPCHAROID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), VARCHAROID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), DATEOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), TIMEOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), TIMESTAMPOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), TIMESTAMPTZOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), INTERVALOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), TIMETZOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), BITOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), VARBITOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
		if (!fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), NUMERICOID, ECPG_ARRAY_NONE, fc_stmt->lineno))
			return ECPG_ARRAY_ERROR;
	}

	for (fc_cache_entry = (fc_stmt->connection->cache_head); fc_cache_entry != NULL; fc_cache_entry = fc_cache_entry->next)
	{
		if (fc_cache_entry->oid == fc_type)
			return fc_cache_entry->isarray;
	}

	fc_array_query = (char *) ecpg_alloc(strlen("select typlen from pg_type where oid= and typelem<>0") + 11, fc_stmt->lineno);
	if (fc_array_query == NULL)
		return ECPG_ARRAY_ERROR;

	sprintf(fc_array_query, "select typlen from pg_type where oid=%d and typelem<>0", fc_type);
	fc_query = PQexec(fc_stmt->connection->connection, fc_array_query);
	ecpg_free(fc_array_query);
	if (!ecpg_check_PQresult(fc_query, fc_stmt->lineno, fc_stmt->connection->connection, fc_stmt->compat))
		return ECPG_ARRAY_ERROR;
	else if (PQresultStatus(fc_query) == PGRES_TUPLES_OK)
	{
		if (PQntuples(fc_query) == 0)
			fc_isarray = ECPG_ARRAY_NONE;
		else
		{
			fc_isarray = (atol((char *) PQgetvalue(fc_query, 0, 0)) == -1) ? ECPG_ARRAY_ARRAY : ECPG_ARRAY_VECTOR;
			if (ecpg_dynamic_type(fc_type) == SQL3_CHARACTER ||
				ecpg_dynamic_type(fc_type) == SQL3_CHARACTER_VARYING)
			{
				/*
				 * 字符串数组尚未实现
				 */
				fc_isarray = ECPG_ARRAY_NONE;
			}
		}
		PQclear(fc_query);
	}
	else
		return ECPG_ARRAY_ERROR;

	fc_ecpg_type_infocache_push(&(fc_stmt->connection->cache_head), fc_type, fc_isarray, fc_stmt->lineno);
	ecpg_log("ecpg_is_type_an_array on line %d: type (%d); C (%d); array (%s)\n", fc_stmt->lineno, fc_type, fc_var->type, ECPG_IS_ARRAY(fc_isarray) ? "yes" : "no");
	return fc_isarray;
}


bool ecpg_store_result(const PGresult *fc_results, int fc_act_field,
				  const struct statement *fc_stmt, struct variable *fc_var)
{
	enum ARRAY_TYPE fc_isarray;
	int			fc_act_tuple,
				fc_ntuples = PQntuples(fc_results);
	bool		fc_status = true;

	if ((fc_isarray = fc_ecpg_is_type_an_array(PQftype(fc_results, fc_act_field), fc_stmt, fc_var)) == ECPG_ARRAY_ERROR)
	{
		ecpg_raise(fc_stmt->lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
		return false;
	}

	if (fc_isarray == ECPG_ARRAY_NONE)
	{
		/*
		 * 如果我们没有足够的空间，就无法读取所有元组
		 */
		if ((fc_var->arrsize > 0 && fc_ntuples > fc_var->arrsize) || (fc_var->ind_arrsize > 0 && fc_ntuples > fc_var->ind_arrsize))
		{
			ecpg_log("ecpg_store_result on line %d: incorrect number of matches; %d don't fit into array of %ld\n",
					 fc_stmt->lineno, fc_ntuples, fc_var->arrsize);
			ecpg_raise(fc_stmt->lineno, INFORMIX_MODE(fc_stmt->compat) ? ECPG_INFORMIX_SUBSELECT_NOT_ONE : ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL);
			return false;
		}
	}
	else
	{
		/*
		 * 由于我们读取的是数组，变量也必须是数组
		 */
		if (fc_var->arrsize == 0)
		{
			ecpg_raise(fc_stmt->lineno, ECPG_NO_ARRAY, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
			return false;
		}
	}

	/*
	 * 为NULL指针分配内存
	 */
	if ((fc_var->arrsize == 0 || fc_var->varcharsize == 0) && fc_var->value == NULL)
	{
		int			fc_len = 0;

		if (!PQfformat(fc_results, fc_act_field))
		{
			switch (fc_var->type)
			{
				case ECPGt_char:
				case ECPGt_unsigned_char:
				case ECPGt_string:
					if (!fc_var->varcharsize && !fc_var->arrsize)
					{
						/* 处理char**foo=0的特殊模式 */
						for (fc_act_tuple = 0; fc_act_tuple < fc_ntuples; fc_act_tuple++)
							fc_len += strlen(PQgetvalue(fc_results, fc_act_tuple, fc_act_field)) + 1;
						fc_len *= fc_var->offset; /* 应该是1，但YMNK */
						fc_len += (fc_ntuples + 1) * sizeof(char *);
					}
					else
					{
						fc_var->varcharsize = 0;
						/* 检查每个元组的strlen */
						for (fc_act_tuple = 0; fc_act_tuple < fc_ntuples; fc_act_tuple++)
						{
							int			fc_len = strlen(PQgetvalue(fc_results, fc_act_tuple, fc_act_field)) + 1;

							if (fc_len > fc_var->varcharsize)
								fc_var->varcharsize = fc_len;
						}
						fc_var->offset *= fc_var->varcharsize;
						fc_len = fc_var->offset * fc_ntuples;
					}
					break;
				case ECPGt_varchar:
					fc_len = fc_ntuples * (fc_var->varcharsize + sizeof(int));
					break;
				default:
					fc_len = fc_var->offset * fc_ntuples;
					break;
			}
		}
		else
		{
			for (fc_act_tuple = 0; fc_act_tuple < fc_ntuples; fc_act_tuple++)
				fc_len += PQgetlength(fc_results, fc_act_tuple, fc_act_field);
		}

		ecpg_log("ecpg_store_result on line %d: allocating memory for %d tuples\n", fc_stmt->lineno, fc_ntuples);
		fc_var->value = (char *) ecpg_auto_alloc(fc_len, fc_stmt->lineno);
		if (!fc_var->value)
			return false;
		*((char **) fc_var->pointer) = fc_var->value;
	}

	/* 如有需要，分配指示变量 */
	if ((fc_var->ind_arrsize == 0 || fc_var->ind_varcharsize == 0) && fc_var->ind_value == NULL && fc_var->ind_pointer != NULL)
	{
		int			fc_len = fc_var->ind_offset * fc_ntuples;

		fc_var->ind_value = (char *) ecpg_auto_alloc(fc_len, fc_stmt->lineno);
		if (!fc_var->ind_value)
			return false;
		*((char **) fc_var->ind_pointer) = fc_var->ind_value;
	}

	/* 用元组填充变量 */
	if (!fc_var->varcharsize && !fc_var->arrsize &&
		(fc_var->type == ECPGt_char || fc_var->type == ECPGt_unsigned_char || fc_var->type == ECPGt_string))
	{
		/* 处理char**foo=0的特殊模式 */

		/* 填充(char*)数组 */
		char	  **fc_current_string = (char **) fc_var->value;

		/* 存储数据（在最后一个数组元素之后） */
		char	   *fc_current_data_location = (char *) &fc_current_string[fc_ntuples + 1];

		for (fc_act_tuple = 0; fc_act_tuple < fc_ntuples && fc_status; fc_act_tuple++)
		{
			int			fc_len = strlen(PQgetvalue(fc_results, fc_act_tuple, fc_act_field)) + 1;

			if (!ecpg_get_data(fc_results, fc_act_tuple, fc_act_field, fc_stmt->lineno,
							   fc_var->type, fc_var->ind_type, fc_current_data_location,
							   fc_var->ind_value, fc_len, 0, fc_var->ind_offset, fc_isarray, fc_stmt->compat, fc_stmt->force_indicator))
				fc_status = false;
			else
			{
				*fc_current_string = fc_current_data_location;
				fc_current_data_location += fc_len;
				fc_current_string++;
			}
		}

		/* 终止列表 */
		*fc_current_string = NULL;
	}
	else
	{
		for (fc_act_tuple = 0; fc_act_tuple < fc_ntuples && fc_status; fc_act_tuple++)
		{
			if (!ecpg_get_data(fc_results, fc_act_tuple, fc_act_field, fc_stmt->lineno,
							   fc_var->type, fc_var->ind_type, fc_var->value,
							   fc_var->ind_value, fc_var->varcharsize, fc_var->offset, fc_var->ind_offset, fc_isarray, fc_stmt->compat, fc_stmt->force_indicator))
				fc_status = false;
		}
	}
	return fc_status;
}

static void fc_sprintf_double_value(char *fc_ptr, double fc_value, const char *fc_delim)
{
	if (isnan(fc_value))
		sprintf(fc_ptr, "%s%s", "NaN", fc_delim);
	else if (isinf(fc_value))
	{
		if (fc_value < 0)
			sprintf(fc_ptr, "%s%s", "-Infinity", fc_delim);
		else
			sprintf(fc_ptr, "%s%s", "Infinity", fc_delim);
	}
	else
		sprintf(fc_ptr, "%.15g%s", fc_value, fc_delim);
}

static void fc_sprintf_float_value(char *fc_ptr, float fc_value, const char *fc_delim)
{
	if (isnan(fc_value))
		sprintf(fc_ptr, "%s%s", "NaN", fc_delim);
	else if (isinf(fc_value))
	{
		if (fc_value < 0)
			sprintf(fc_ptr, "%s%s", "-Infinity", fc_delim);
		else
			sprintf(fc_ptr, "%s%s", "Infinity", fc_delim);
	}
	else
		sprintf(fc_ptr, "%.15g%s", fc_value, fc_delim);
}

static char * fc_convert_bytea_to_string(char *fc_from_data, int fc_from_len, int fc_lineno)
{
	char	   *fc_to_data;
	int			fc_to_len = ecpg_hex_enc_len(fc_from_len) + 4 + 1;	/* 反斜杠 + 'x' +
																 * 引用 + 引用 */

	fc_to_data = ecpg_alloc(fc_to_len, fc_lineno);
	if (!fc_to_data)
		return NULL;

	strcpy(fc_to_data, "'\\x");
	ecpg_hex_encode(fc_from_data, fc_from_len, fc_to_data + 3);
	strcpy(fc_to_data + 3 + ecpg_hex_enc_len(fc_from_len), "\'");

	return fc_to_data;
}

bool ecpg_store_input(const int fc_lineno, const bool fc_force_indicator, const struct variable *fc_var,
				 char **fc_tobeinserted_p, bool fc_quote)
{
	char	   *fc_mallocedval = NULL;
	char	   *fc_newcopy = NULL;

	/*
	 * 除非列也是数组，否则数组是不可能的。FIXME：我们不知道
	 * 列是一个数组，输入数组到单例列将导致运行时错误。
	 */

	/*
	 * 对于记录需要一些特殊处理，因为我们想要它们的
	 * 内容以逗号分隔的列表形式插入（我想）。
	 */

	*fc_tobeinserted_p = "";

	/* 检查空值并相应地设置输入缓冲区 */
	switch (fc_var->ind_type)
	{
		case ECPGt_short:
		case ECPGt_unsigned_short:
			if (*(short *) fc_var->ind_value < 0)
				*fc_tobeinserted_p = NULL;
			break;
		case ECPGt_int:
		case ECPGt_unsigned_int:
			if (*(int *) fc_var->ind_value < 0)
				*fc_tobeinserted_p = NULL;
			break;
		case ECPGt_long:
		case ECPGt_unsigned_long:
			if (*(long *) fc_var->ind_value < 0L)
				*fc_tobeinserted_p = NULL;
			break;
		case ECPGt_long_long:
		case ECPGt_unsigned_long_long:
			if (*(long long int *) fc_var->ind_value < (long long) 0)
				*fc_tobeinserted_p = NULL;
			break;
		case ECPGt_NO_INDICATOR:
			if (fc_force_indicator == false)
			{
				if (ECPGis_noind_null(fc_var->type, fc_var->value))
					*fc_tobeinserted_p = NULL;
			}
			break;
		default:
			break;
	}
	if (*fc_tobeinserted_p != NULL)
	{
		int			fc_asize = fc_var->arrsize ? fc_var->arrsize : 1;

		switch (fc_var->type)
		{
				int			fc_element;

			case ECPGt_short:
				if (!(fc_mallocedval = ecpg_alloc(fc_asize * 20, fc_lineno)))
					return false;

				if (fc_asize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						sprintf(fc_mallocedval + strlen(fc_mallocedval), "%hd,", ((short *) fc_var->value)[fc_element]);

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
					sprintf(fc_mallocedval, "%hd", *((short *) fc_var->value));

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_int:
				if (!(fc_mallocedval = ecpg_alloc(fc_asize * 20, fc_lineno)))
					return false;

				if (fc_asize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						sprintf(fc_mallocedval + strlen(fc_mallocedval), "%d,", ((int *) fc_var->value)[fc_element]);

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
					sprintf(fc_mallocedval, "%d", *((int *) fc_var->value));

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_unsigned_short:
				if (!(fc_mallocedval = ecpg_alloc(fc_asize * 20, fc_lineno)))
					return false;

				if (fc_asize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						sprintf(fc_mallocedval + strlen(fc_mallocedval), "%hu,", ((unsigned short *) fc_var->value)[fc_element]);

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
					sprintf(fc_mallocedval, "%hu", *((unsigned short *) fc_var->value));

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_unsigned_int:
				if (!(fc_mallocedval = ecpg_alloc(fc_asize * 20, fc_lineno)))
					return false;

				if (fc_asize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						sprintf(fc_mallocedval + strlen(fc_mallocedval), "%u,", ((unsigned int *) fc_var->value)[fc_element]);

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
					sprintf(fc_mallocedval, "%u", *((unsigned int *) fc_var->value));

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_long:
				if (!(fc_mallocedval = ecpg_alloc(fc_asize * 20, fc_lineno)))
					return false;

				if (fc_asize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						sprintf(fc_mallocedval + strlen(fc_mallocedval), "%ld,", ((long *) fc_var->value)[fc_element]);

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
					sprintf(fc_mallocedval, "%ld", *((long *) fc_var->value));

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_unsigned_long:
				if (!(fc_mallocedval = ecpg_alloc(fc_asize * 20, fc_lineno)))
					return false;

				if (fc_asize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						sprintf(fc_mallocedval + strlen(fc_mallocedval), "%lu,", ((unsigned long *) fc_var->value)[fc_element]);

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
					sprintf(fc_mallocedval, "%lu", *((unsigned long *) fc_var->value));

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_long_long:
				if (!(fc_mallocedval = ecpg_alloc(fc_asize * 30, fc_lineno)))
					return false;

				if (fc_asize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						sprintf(fc_mallocedval + strlen(fc_mallocedval), "%lld,", ((long long int *) fc_var->value)[fc_element]);

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
					sprintf(fc_mallocedval, "%lld", *((long long int *) fc_var->value));

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_unsigned_long_long:
				if (!(fc_mallocedval = ecpg_alloc(fc_asize * 30, fc_lineno)))
					return false;

				if (fc_asize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						sprintf(fc_mallocedval + strlen(fc_mallocedval), "%llu,", ((unsigned long long int *) fc_var->value)[fc_element]);

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
					sprintf(fc_mallocedval, "%llu", *((unsigned long long int *) fc_var->value));

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_float:
				if (!(fc_mallocedval = ecpg_alloc(fc_asize * 25, fc_lineno)))
					return false;

				if (fc_asize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						fc_sprintf_float_value(fc_mallocedval + strlen(fc_mallocedval), ((float *) fc_var->value)[fc_element], ",");

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
					fc_sprintf_float_value(fc_mallocedval, *((float *) fc_var->value), "");

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_double:
				if (!(fc_mallocedval = ecpg_alloc(fc_asize * 25, fc_lineno)))
					return false;

				if (fc_asize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						fc_sprintf_double_value(fc_mallocedval + strlen(fc_mallocedval), ((double *) fc_var->value)[fc_element], ",");

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
					fc_sprintf_double_value(fc_mallocedval, *((double *) fc_var->value), "");

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_bool:
				if (!(fc_mallocedval = ecpg_alloc(fc_var->arrsize + sizeof("{}"), fc_lineno)))
					return false;

				if (fc_var->arrsize > 1)
				{
					strcpy(fc_mallocedval, "{");

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
						sprintf(fc_mallocedval + strlen(fc_mallocedval), "%c,", (((bool *) fc_var->value)[fc_element]) ? 't' : 'f');

					strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");
				}
				else
				{
					if (fc_var->offset == sizeof(char))
						sprintf(fc_mallocedval, "%c", (*((char *) fc_var->value)) ? 't' : 'f');
					else if (fc_var->offset == sizeof(int))
						sprintf(fc_mallocedval, "%c", (*((int *) fc_var->value)) ? 't' : 'f');
					else
						ecpg_raise(fc_lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
				}

				*fc_tobeinserted_p = fc_mallocedval;
				break;

			case ECPGt_char:
			case ECPGt_unsigned_char:
			case ECPGt_string:
				{
					/* 如果类型为char *，则将slen设置为字符串长度 */
					int			fc_slen = (fc_var->varcharsize == 0) ? strlen((char *) fc_var->value) : (unsigned int) fc_var->varcharsize;

					if (!(fc_newcopy = ecpg_alloc(fc_slen + 1, fc_lineno)))
						return false;

					strncpy(fc_newcopy, (char *) fc_var->value, fc_slen);
					fc_newcopy[fc_slen] = '\0';

					fc_mallocedval = fc_quote_postgres(fc_newcopy, fc_quote, fc_lineno);
					if (!fc_mallocedval)
					{
						ecpg_free(fc_newcopy);
						return false;
					}

					*fc_tobeinserted_p = fc_mallocedval;
				}
				break;
			case ECPGt_const:
			case ECPGt_char_variable:
				{
					int			fc_slen = strlen((char *) fc_var->value);

					if (!(fc_mallocedval = ecpg_alloc(fc_slen + 1, fc_lineno)))
						return false;

					strncpy(fc_mallocedval, (char *) fc_var->value, fc_slen);
					fc_mallocedval[fc_slen] = '\0';

					*fc_tobeinserted_p = fc_mallocedval;
				}
				break;

			case ECPGt_bytea:
				{
					struct ECPGgeneric_bytea *fc_variable =
					(struct ECPGgeneric_bytea *) (fc_var->value);

					if (!(fc_mallocedval = (char *) ecpg_alloc(fc_variable->len, fc_lineno)))
						return false;

					memcpy(fc_mallocedval, fc_variable->arr, fc_variable->len);
					*fc_tobeinserted_p = fc_mallocedval;
				}
				break;

			case ECPGt_varchar:
				{
					struct ECPGgeneric_varchar *fc_variable =
					(struct ECPGgeneric_varchar *) (fc_var->value);

					if (!(fc_newcopy = (char *) ecpg_alloc(fc_variable->len + 1, fc_lineno)))
						return false;

					strncpy(fc_newcopy, fc_variable->arr, fc_variable->len);
					fc_newcopy[fc_variable->len] = '\0';

					fc_mallocedval = fc_quote_postgres(fc_newcopy, fc_quote, fc_lineno);
					if (!fc_mallocedval)
					{
						ecpg_free(fc_newcopy);
						return false;
					}

					*fc_tobeinserted_p = fc_mallocedval;
				}
				break;

			case ECPGt_decimal:
			case ECPGt_numeric:
				{
					char	   *fc_str = NULL;
					int			fc_slen;
					numeric    *fc_nval;

					if (fc_var->arrsize > 1)
						fc_mallocedval = ecpg_strdup("{", fc_lineno);
					else
						fc_mallocedval = ecpg_strdup("", fc_lineno);

					if (!fc_mallocedval)
						return false;

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
					{
						int			fc_result;

						fc_nval = PGTYPESnumeric_new();
						if (!fc_nval)
						{
							ecpg_free(fc_mallocedval);
							return false;
						}

						if (fc_var->type == ECPGt_numeric)
							fc_result = PGTYPESnumeric_copy(&(((numeric *) (fc_var->value))[fc_element]), fc_nval);
						else
							fc_result = PGTYPESnumeric_from_decimal(&(((decimal *) (fc_var->value))[fc_element]), fc_nval);

						if (fc_result != 0)
						{
							PGTYPESnumeric_free(fc_nval);
							ecpg_free(fc_mallocedval);
							return false;
						}

						fc_str = PGTYPESnumeric_to_asc(fc_nval, fc_nval->dscale);
						fc_slen = strlen(fc_str);
						PGTYPESnumeric_free(fc_nval);

						if (!(fc_newcopy = ecpg_realloc(fc_mallocedval, strlen(fc_mallocedval) + fc_slen + 2, fc_lineno)))
						{
							ecpg_free(fc_mallocedval);
							ecpg_free(fc_str);
							return false;
						}
						fc_mallocedval = fc_newcopy;

						/* 也复制尾部的'\0' */
						memcpy(fc_mallocedval + strlen(fc_mallocedval), fc_str, fc_slen + 1);
						if (fc_var->arrsize > 1)
							strcpy(fc_mallocedval + strlen(fc_mallocedval), ",");

						ecpg_free(fc_str);
					}

					if (fc_var->arrsize > 1)
						strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");

					*fc_tobeinserted_p = fc_mallocedval;
				}
				break;

			case ECPGt_interval:
				{
					char	   *fc_str = NULL;
					int			fc_slen;

					if (fc_var->arrsize > 1)
						fc_mallocedval = ecpg_strdup("{", fc_lineno);
					else
						fc_mallocedval = ecpg_strdup("", fc_lineno);

					if (!fc_mallocedval)
						return false;

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
					{
						fc_str = fc_quote_postgres(PGTYPESinterval_to_asc(&(((interval *) (fc_var->value))[fc_element])), fc_quote, fc_lineno);
						if (!fc_str)
						{
							ecpg_free(fc_mallocedval);
							return false;
						}

						fc_slen = strlen(fc_str);

						if (!(fc_newcopy = ecpg_realloc(fc_mallocedval, strlen(fc_mallocedval) + fc_slen + 2, fc_lineno)))
						{
							ecpg_free(fc_mallocedval);
							ecpg_free(fc_str);
							return false;
						}
						fc_mallocedval = fc_newcopy;

						/* 也复制尾部的'\0' */
						memcpy(fc_mallocedval + strlen(fc_mallocedval), fc_str, fc_slen + 1);
						if (fc_var->arrsize > 1)
							strcpy(fc_mallocedval + strlen(fc_mallocedval), ",");

						ecpg_free(fc_str);
					}

					if (fc_var->arrsize > 1)
						strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");

					*fc_tobeinserted_p = fc_mallocedval;
				}
				break;

			case ECPGt_date:
				{
					char	   *fc_str = NULL;
					int			fc_slen;

					if (fc_var->arrsize > 1)
						fc_mallocedval = ecpg_strdup("{", fc_lineno);
					else
						fc_mallocedval = ecpg_strdup("", fc_lineno);

					if (!fc_mallocedval)
						return false;

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
					{
						fc_str = fc_quote_postgres(PGTYPESdate_to_asc(((date *) (fc_var->value))[fc_element]), fc_quote, fc_lineno);
						if (!fc_str)
						{
							ecpg_free(fc_mallocedval);
							return false;
						}

						fc_slen = strlen(fc_str);

						if (!(fc_newcopy = ecpg_realloc(fc_mallocedval, strlen(fc_mallocedval) + fc_slen + 2, fc_lineno)))
						{
							ecpg_free(fc_mallocedval);
							ecpg_free(fc_str);
							return false;
						}
						fc_mallocedval = fc_newcopy;

						/* 也复制尾部的'\0' */
						memcpy(fc_mallocedval + strlen(fc_mallocedval), fc_str, fc_slen + 1);
						if (fc_var->arrsize > 1)
							strcpy(fc_mallocedval + strlen(fc_mallocedval), ",");

						ecpg_free(fc_str);
					}

					if (fc_var->arrsize > 1)
						strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");

					*fc_tobeinserted_p = fc_mallocedval;
				}
				break;

			case ECPGt_timestamp:
				{
					char	   *fc_str = NULL;
					int			fc_slen;

					if (fc_var->arrsize > 1)
						fc_mallocedval = ecpg_strdup("{", fc_lineno);
					else
						fc_mallocedval = ecpg_strdup("", fc_lineno);

					if (!fc_mallocedval)
						return false;

					for (fc_element = 0; fc_element < fc_asize; fc_element++)
					{
						fc_str = fc_quote_postgres(PGTYPEStimestamp_to_asc(((timestamp *) (fc_var->value))[fc_element]), fc_quote, fc_lineno);
						if (!fc_str)
						{
							ecpg_free(fc_mallocedval);
							return false;
						}

						fc_slen = strlen(fc_str);

						if (!(fc_newcopy = ecpg_realloc(fc_mallocedval, strlen(fc_mallocedval) + fc_slen + 2, fc_lineno)))
						{
							ecpg_free(fc_mallocedval);
							ecpg_free(fc_str);
							return false;
						}
						fc_mallocedval = fc_newcopy;

						/* 也复制尾部的'\0' */
						memcpy(fc_mallocedval + strlen(fc_mallocedval), fc_str, fc_slen + 1);
						if (fc_var->arrsize > 1)
							strcpy(fc_mallocedval + strlen(fc_mallocedval), ",");

						ecpg_free(fc_str);
					}

					if (fc_var->arrsize > 1)
						strcpy(fc_mallocedval + strlen(fc_mallocedval) - 1, "}");

					*fc_tobeinserted_p = fc_mallocedval;
				}
				break;

			case ECPGt_descriptor:
			case ECPGt_sqlda:
				break;

			default:
				/* 尚未实现 */
				ecpg_raise(fc_lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, ecpg_type_name(fc_var->type));
				return false;
				break;
		}
	}
	return true;
}

static void fc_print_param_value(char *fc_value, int fc_len, int fc_is_binary, int fc_lineno, int fc_nth)
{
	char	   *fc_value_s;
	bool		fc_malloced = false;

	if (fc_value == NULL)
		fc_value_s = "null";
	else if (!fc_is_binary)
		fc_value_s = fc_value;
	else
	{
		fc_value_s = ecpg_alloc(ecpg_hex_enc_len(fc_len) + 1, fc_lineno);
		if (fc_value_s != NULL)
		{
			ecpg_hex_encode(fc_value, fc_len, fc_value_s);
			fc_value_s[ecpg_hex_enc_len(fc_len)] = '\0';
			fc_malloced = true;
		}
		else
			fc_value_s = "no memory for logging of parameter";
	}

	ecpg_log("ecpg_free_params on line %d: parameter %d = %s\n",
			 fc_lineno, fc_nth, fc_value_s);

	if (fc_malloced)
		ecpg_free(fc_value_s);
}

void ecpg_free_params(struct statement *fc_stmt, bool fc_print)
{
	int			fc_n;

	for (fc_n = 0; fc_n < fc_stmt->nparams; fc_n++)
	{
		if (fc_print)
			fc_print_param_value(fc_stmt->paramvalues[fc_n], fc_stmt->paramlengths[fc_n],
							  fc_stmt->paramformats[fc_n], fc_stmt->lineno, fc_n + 1);
		ecpg_free(fc_stmt->paramvalues[fc_n]);
	}
	ecpg_free(fc_stmt->paramvalues);
	ecpg_free(fc_stmt->paramlengths);
	ecpg_free(fc_stmt->paramformats);
	fc_stmt->paramvalues = NULL;
	fc_stmt->paramlengths = NULL;
	fc_stmt->paramformats = NULL;
	fc_stmt->nparams = 0;
}

static bool fc_insert_tobeinserted(int fc_position, int fc_ph_len, struct statement *fc_stmt, char *fc_tobeinserted)
{
	char	   *fc_newcopy;

	if (!(fc_newcopy = (char *) ecpg_alloc(strlen(fc_stmt->command)
										+ strlen(fc_tobeinserted)
										+ 1, fc_stmt->lineno)))
	{
		ecpg_free(fc_tobeinserted);
		return false;
	}

	strcpy(fc_newcopy, fc_stmt->command);
	strcpy(fc_newcopy + fc_position - 1, fc_tobeinserted);

	/*
	 * 第二个参数中的奇怪之处是旧字符串的其余部分
	 */
	strcat(fc_newcopy,
		   fc_stmt->command
		   + fc_position
		   + fc_ph_len - 1);

	ecpg_free(fc_stmt->command);
	fc_stmt->command = fc_newcopy;

	ecpg_free(fc_tobeinserted);
	return true;
}

static bool fc_store_input_from_desc(struct statement *fc_stmt, struct descriptor_item *fc_desc_item,
					  char **fc_tobeinserted)
{
	struct variable fc_var;

	/*
	 * 在处理二进制数据时，仅分配内存并使用memcpy，因为二进制
	 * 数据已通过ecpg_store_input()在ECPGset_desc()中存储到desc_item->data中。
	 */
	if (fc_desc_item->is_binary)
	{
		if (!(*fc_tobeinserted = ecpg_alloc(fc_desc_item->data_len, fc_stmt->lineno)))
			return false;
		memcpy(*fc_tobeinserted, fc_desc_item->data, fc_desc_item->data_len);
		return true;
	}

	fc_var.type = ECPGt_char;
	fc_var.varcharsize = strlen(fc_desc_item->data);
	fc_var.value = fc_desc_item->data;
	fc_var.pointer = &(fc_desc_item->data);
	fc_var.arrsize = 1;
	fc_var.offset = 0;

	if (!fc_desc_item->indicator)
	{
		fc_var.ind_type = ECPGt_NO_INDICATOR;
		fc_var.ind_value = fc_var.ind_pointer = NULL;
		fc_var.ind_varcharsize = fc_var.ind_arrsize = fc_var.ind_offset = 0;
	}
	else
	{
		fc_var.ind_type = ECPGt_int;
		fc_var.ind_value = &(fc_desc_item->indicator);
		fc_var.ind_pointer = &(fc_var.ind_value);
		fc_var.ind_varcharsize = fc_var.ind_arrsize = 1;
		fc_var.ind_offset = 0;
	}

	if (!ecpg_store_input(fc_stmt->lineno, fc_stmt->force_indicator, &fc_var, fc_tobeinserted, false))
		return false;

	return true;
}

/*
 * ecpg_build_params
 *		构建语句参数
 *
 * 输入值来自用户变量，结果存储在可以被PQexecParams()使用的数组中。
 */
bool ecpg_build_params(struct statement *fc_stmt)
{
	struct variable *fc_var;
	int			fc_desc_counter = 0;
	int			fc_position = 0;
	const char *fc_value;
	bool		fc_std_strings = false;

	/* 获取standard_conforming_strings设置。 */
	fc_value = PQparameterStatus(fc_stmt->connection->connection, "standard_conforming_strings");
	if (fc_value && strcmp(fc_value, "on") == 0)
		fc_std_strings = true;

	/*
	 * 如果类型是填充类型之一，那么我们将参数放入参数数组的第一个位置。
	 * 然后，如果还有更多的填充类型，我们将添加更多参数。
	 */
	fc_var = fc_stmt->inlist;
	while (fc_var)
	{
		char	   *fc_tobeinserted;
		int			fc_counter = 1;
		bool		fc_binary_format;
		int			fc_binary_length;


		fc_tobeinserted = NULL;
		fc_binary_length = 0;
		fc_binary_format = false;

		/*
		 * 描述符是一个特例，因为它包含许多变量但只列出一次。
		 */
		if (fc_var->type == ECPGt_descriptor)
		{
			/*
			 * 在这里我们创建一个额外的变量列表，因此相同的逻辑适用。
			 */
			struct descriptor *fc_desc;
			struct descriptor_item *fc_desc_item;

			fc_desc = ecpg_find_desc(fc_stmt->lineno, fc_var->pointer);
			if (fc_desc == NULL)
				return false;

			fc_desc_counter++;
			for (fc_desc_item = fc_desc->items; fc_desc_item; fc_desc_item = fc_desc_item->next)
			{
				if (fc_desc_item->num != fc_desc_counter)
					continue;

				if (!fc_store_input_from_desc(fc_stmt, fc_desc_item, &fc_tobeinserted))
					return false;

				if (fc_desc_item->is_binary)
				{
					fc_binary_length = fc_desc_item->data_len;
					fc_binary_format = true;
				}
				break;
			}
			if (fc_desc->count == fc_desc_counter)
				fc_desc_counter = 0;
		}
		else if (fc_var->type == ECPGt_sqlda)
		{
			if (INFORMIX_MODE(fc_stmt->compat))
			{
				struct sqlda_compat *fc_sqlda = *(struct sqlda_compat **) fc_var->pointer;
				struct variable fc_desc_inlist;
				int			fc_i;

				if (fc_sqlda == NULL)
					return false;

				fc_desc_counter++;
				for (fc_i = 0; fc_i < fc_sqlda->sqld; fc_i++)
				{
					if (fc_i + 1 == fc_desc_counter)
					{
						fc_desc_inlist.type = fc_sqlda->sqlvar[fc_i].sqltype;
						fc_desc_inlist.value = fc_sqlda->sqlvar[fc_i].sqldata;
						fc_desc_inlist.pointer = &(fc_sqlda->sqlvar[fc_i].sqldata);
						switch (fc_desc_inlist.type)
						{
							case ECPGt_char:
							case ECPGt_varchar:
								fc_desc_inlist.varcharsize = strlen(fc_sqlda->sqlvar[fc_i].sqldata);
								break;
							default:
								fc_desc_inlist.varcharsize = 0;
								break;
						}
						fc_desc_inlist.arrsize = 1;
						fc_desc_inlist.offset = 0;
						if (fc_sqlda->sqlvar[fc_i].sqlind)
						{
							fc_desc_inlist.ind_type = ECPGt_short;
							/* ECPG期望指示值 < 0 */
							if (*(fc_sqlda->sqlvar[fc_i].sqlind))
								*(fc_sqlda->sqlvar[fc_i].sqlind) = -1;
							fc_desc_inlist.ind_value = fc_sqlda->sqlvar[fc_i].sqlind;
							fc_desc_inlist.ind_pointer = &(fc_sqlda->sqlvar[fc_i].sqlind);
							fc_desc_inlist.ind_varcharsize = fc_desc_inlist.ind_arrsize = 1;
							fc_desc_inlist.ind_offset = 0;
						}
						else
						{
							fc_desc_inlist.ind_type = ECPGt_NO_INDICATOR;
							fc_desc_inlist.ind_value = fc_desc_inlist.ind_pointer = NULL;
							fc_desc_inlist.ind_varcharsize = fc_desc_inlist.ind_arrsize = fc_desc_inlist.ind_offset = 0;
						}
						if (!ecpg_store_input(fc_stmt->lineno, fc_stmt->force_indicator, &fc_desc_inlist, &fc_tobeinserted, false))
							return false;

						break;
					}
				}
				if (fc_sqlda->sqld == fc_desc_counter)
					fc_desc_counter = 0;
			}
			else
			{
				struct sqlda_struct *fc_sqlda = *(struct sqlda_struct **) fc_var->pointer;
				struct variable fc_desc_inlist;
				int			fc_i;

				if (fc_sqlda == NULL)
					return false;

				fc_desc_counter++;
				for (fc_i = 0; fc_i < fc_sqlda->sqln; fc_i++)
				{
					if (fc_i + 1 == fc_desc_counter)
					{
						fc_desc_inlist.type = fc_sqlda->sqlvar[fc_i].sqltype;
						fc_desc_inlist.value = fc_sqlda->sqlvar[fc_i].sqldata;
						fc_desc_inlist.pointer = &(fc_sqlda->sqlvar[fc_i].sqldata);
						switch (fc_desc_inlist.type)
						{
							case ECPGt_char:
							case ECPGt_varchar:
								fc_desc_inlist.varcharsize = strlen(fc_sqlda->sqlvar[fc_i].sqldata);
								break;
							default:
								fc_desc_inlist.varcharsize = 0;
								break;
						}
						fc_desc_inlist.arrsize = 1;
						fc_desc_inlist.offset = 0;
						if (fc_sqlda->sqlvar[fc_i].sqlind)
						{
							fc_desc_inlist.ind_type = ECPGt_short;
							/* ECPG期望指示值 < 0 */
							if (*(fc_sqlda->sqlvar[fc_i].sqlind))
								*(fc_sqlda->sqlvar[fc_i].sqlind) = -1;
							fc_desc_inlist.ind_value = fc_sqlda->sqlvar[fc_i].sqlind;
							fc_desc_inlist.ind_pointer = &(fc_sqlda->sqlvar[fc_i].sqlind);
							fc_desc_inlist.ind_varcharsize = fc_desc_inlist.ind_arrsize = 1;
							fc_desc_inlist.ind_offset = 0;
						}
						else
						{
							fc_desc_inlist.ind_type = ECPGt_NO_INDICATOR;
							fc_desc_inlist.ind_value = fc_desc_inlist.ind_pointer = NULL;
							fc_desc_inlist.ind_varcharsize = fc_desc_inlist.ind_arrsize = fc_desc_inlist.ind_offset = 0;
						}
						if (!ecpg_store_input(fc_stmt->lineno, fc_stmt->force_indicator, &fc_desc_inlist, &fc_tobeinserted, false))
							return false;

						break;
					}
				}
				if (fc_sqlda->sqln == fc_desc_counter)
					fc_desc_counter = 0;
			}
		}
		else
		{
			if (!ecpg_store_input(fc_stmt->lineno, fc_stmt->force_indicator, fc_var, &fc_tobeinserted, false))
				return false;

			if (fc_var->type == ECPGt_bytea)
			{
				fc_binary_length = ((struct ECPGgeneric_bytea *) (fc_var->value))->len;
				fc_binary_format = true;
			}
		}

		/*
		 * 现在tobeinserted指向一个包含下一个参数的区域；
		 * 现在找到它在字符串中属于的位置
		 */
		if ((fc_position = fc_next_insert(fc_stmt->command, fc_position, fc_stmt->questionmarks, fc_std_strings) + 1) == 0)
		{
			/*
			 * 我们有一个参数，但在字符串中没有匹配的占位符
			 */
			ecpg_raise(fc_stmt->lineno, ECPG_TOO_MANY_ARGUMENTS,
					   ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS,
					   NULL);
			ecpg_free_params(fc_stmt, false);
			ecpg_free(fc_tobeinserted);
			return false;
		}

		/*
		 * 如果var->type=ECPGt_char_variable，我们有一个动态游标，
		 * 我们必须模拟一个动态游标，因为没有后端功能支持它
		 */
		if (fc_var->type == ECPGt_char_variable)
		{
			int			fc_ph_len = (fc_stmt->command[fc_position] == '?') ? strlen("?") : strlen("$1");

			if (!fc_insert_tobeinserted(fc_position, fc_ph_len, fc_stmt, fc_tobeinserted))
			{
				ecpg_free_params(fc_stmt, false);
				return false;
			}
			fc_tobeinserted = NULL;
		}

		/*
		 * 如果占位符是'$0'，我们必须在客户端替换它，
		 * 这是为了我们想要支持变量但后端不支持的地方
		 */
		else if (fc_stmt->command[fc_position] == '0')
		{
			if (fc_stmt->statement_type == ECPGst_prepare ||
				fc_stmt->statement_type == ECPGst_exec_with_exprlist)
			{
				/* 需要对插入的语句名称进行双引号处理。 */
				char	   *fc_str = ecpg_alloc(strlen(fc_tobeinserted) + 2 + 1,
											 fc_stmt->lineno);

				if (!fc_str)
				{
					ecpg_free(fc_tobeinserted);
					ecpg_free_params(fc_stmt, false);
					return false;
				}
				sprintf(fc_str, "\"%s\"", fc_tobeinserted);
				ecpg_free(fc_tobeinserted);
				fc_tobeinserted = fc_str;
			}

			if (!fc_insert_tobeinserted(fc_position, 2, fc_stmt, fc_tobeinserted))
			{
				ecpg_free_params(fc_stmt, false);
				return false;
			}
			fc_tobeinserted = NULL;
		}
		else if (fc_stmt->statement_type == ECPGst_exec_with_exprlist)
		{
			if (fc_binary_format)
			{
				char	   *fc_p = fc_convert_bytea_to_string(fc_tobeinserted,
														fc_binary_length,
														fc_stmt->lineno);

				ecpg_free(fc_tobeinserted);
				if (!fc_p)
				{
					ecpg_free_params(fc_stmt, false);
					return false;
				}
				fc_tobeinserted = fc_p;
			}

			if (!fc_insert_tobeinserted(fc_position, 2, fc_stmt, fc_tobeinserted))
			{
				ecpg_free_params(fc_stmt, false);
				return false;
			}
			fc_tobeinserted = NULL;
		}
		else
		{
			bool		fc_realloc_failed = false;
			char	  **fc_newparamvalues;
			int		   *fc_newparamlengths;
			int		   *fc_newparamformats;

			/* 扩大所有参数数组 */
			if ((fc_newparamvalues = (char **) ecpg_realloc(fc_stmt->paramvalues, sizeof(char *) * (fc_stmt->nparams + 1), fc_stmt->lineno)))
				fc_stmt->paramvalues = fc_newparamvalues;
			else
				fc_realloc_failed = true;

			if ((fc_newparamlengths = (int *) ecpg_realloc(fc_stmt->paramlengths, sizeof(int) * (fc_stmt->nparams + 1), fc_stmt->lineno)))
				fc_stmt->paramlengths = fc_newparamlengths;
			else
				fc_realloc_failed = true;

			if ((fc_newparamformats = (int *) ecpg_realloc(fc_stmt->paramformats, sizeof(int) * (fc_stmt->nparams + 1), fc_stmt->lineno)))
				fc_stmt->paramformats = fc_newparamformats;
			else
				fc_realloc_failed = true;

			if (fc_realloc_failed)
			{
				ecpg_free_params(fc_stmt, false);
				ecpg_free(fc_tobeinserted);
				return false;
			}

			/* 只有现在我们才能将“tobeinserted”的所有权分配给stmt */
			fc_stmt->paramvalues[fc_stmt->nparams] = fc_tobeinserted;
			fc_stmt->paramlengths[fc_stmt->nparams] = fc_binary_length;
			fc_stmt->paramformats[fc_stmt->nparams] = (fc_binary_format ? 1 : 0);
			fc_stmt->nparams++;

			/* 让我们看看这是否是旧式占位符 */
			if (fc_stmt->command[fc_position] == '?')
			{
				/* 是的，替换为新式 */
				int			fc_buffersize = sizeof(int) * CHAR_BIT * 10 / 3;	/* 我们所需大小的粗略估计 */

				if (!(fc_tobeinserted = (char *) ecpg_alloc(fc_buffersize, fc_stmt->lineno)))
				{
					ecpg_free_params(fc_stmt, false);
					return false;
				}

				snprintf(fc_tobeinserted, fc_buffersize, "$%d", fc_counter++);

				if (!fc_insert_tobeinserted(fc_position, 2, fc_stmt, fc_tobeinserted))
				{
					ecpg_free_params(fc_stmt, false);
					return false;
				}
				fc_tobeinserted = NULL;
			}
		}

		if (fc_desc_counter == 0)
			fc_var = fc_var->next;
	}

	/*
	 * 检查是否还有未匹配的内容。PREPARE AS没有参数。
	 * 检查其他语句。
	 */
	if (fc_stmt->statement_type != ECPGst_prepare &&
		fc_next_insert(fc_stmt->command, fc_position, fc_stmt->questionmarks, fc_std_strings) >= 0)
	{
		ecpg_raise(fc_stmt->lineno, ECPG_TOO_FEW_ARGUMENTS,
				   ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
		ecpg_free_params(fc_stmt, false);
		return false;
	}

	return true;
}

/*
 * ecpg_autostart_transaction
 *		如果我们处于非自动提交模式，则自动启动事务。
 */
bool ecpg_autostart_transaction(struct statement *fc_stmt)
{
	if (PQtransactionStatus(fc_stmt->connection->connection) == PQTRANS_IDLE && !fc_stmt->connection->autocommit)
	{
		fc_stmt->results = PQexec(fc_stmt->connection->connection, "begin transaction");
		if (!ecpg_check_PQresult(fc_stmt->results, fc_stmt->lineno, fc_stmt->connection->connection, fc_stmt->compat))
		{
			ecpg_free_params(fc_stmt, false);
			return false;
		}
		PQclear(fc_stmt->results);
		fc_stmt->results = NULL;
	}
	return true;
}

/*
 * ecpg_execute
 *		执行SQL语句。
 */
bool ecpg_execute(struct statement *fc_stmt)
{
	ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", fc_stmt->lineno, fc_stmt->command, fc_stmt->nparams, fc_stmt->connection->name);
	if (fc_stmt->statement_type == ECPGst_execute)
	{
		fc_stmt->results = PQexecPrepared(fc_stmt->connection->connection,
									   fc_stmt->name,
									   fc_stmt->nparams,
									   (const char *const *) fc_stmt->paramvalues,
									   (const int *) fc_stmt->paramlengths,
									   (const int *) fc_stmt->paramformats,
									   0);
		ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", fc_stmt->lineno, fc_stmt->command);
	}
	else
	{
		if (fc_stmt->nparams == 0)
		{
			fc_stmt->results = PQexec(fc_stmt->connection->connection, fc_stmt->command);
			ecpg_log("ecpg_execute on line %d: using PQexec\n", fc_stmt->lineno);
		}
		else
		{
			fc_stmt->results = PQexecParams(fc_stmt->connection->connection,
										 fc_stmt->command, fc_stmt->nparams, NULL,
										 (const char *const *) fc_stmt->paramvalues,
										 (const int *) fc_stmt->paramlengths,
										 (const int *) fc_stmt->paramformats,
										 0);

			ecpg_log("ecpg_execute on line %d: using PQexecParams\n", fc_stmt->lineno);
		}

		if (fc_stmt->statement_type == ECPGst_prepare)
		{
			if (!ecpg_register_prepared_stmt(fc_stmt))
			{
				ecpg_free_params(fc_stmt, true);
				return false;
			}
		}
	}

	ecpg_free_params(fc_stmt, true);

	if (!ecpg_check_PQresult(fc_stmt->results, fc_stmt->lineno, fc_stmt->connection->connection, fc_stmt->compat))
		return false;

	return true;
}

/*-------
 * ecpg_process_output
 *
 *	处理语句结果并将其存储到应用程序变量中。此
 *	函数可以在同一语句中重复调用，以防使用游标
 *	预读取并且应用程序执行FETCH N导致预读取窗口溢出。
 *
 * 参数
 *	stmt	包含PGresult和输出变量列表的语句结构
 *	clear_result
 *			从此函数返回时PQclear()结果
 *
 * 返回成功作为布尔值。如果失败，则引发SQL错误。
 *-------
 */
bool ecpg_process_output(struct statement *fc_stmt, bool fc_clear_result)
{
	struct variable *fc_var;
	bool		fc_status = false;
	char	   *fc_cmdstat;
	PGnotify   *fc_notify;
	struct sqlca_t *fc_sqlca = ECPGget_sqlca();
	int			fc_nfields,
				fc_ntuples,
				fc_act_field;

	if (fc_sqlca == NULL)
	{
		ecpg_raise(fc_stmt->lineno, ECPG_OUT_OF_MEMORY,
				   ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
		return false;
	}

	fc_var = fc_stmt->outlist;
	switch (PQresultStatus(fc_stmt->results))
	{
		case PGRES_TUPLES_OK:
			fc_nfields = PQnfields(fc_stmt->results);
			fc_sqlca->sqlerrd[2] = fc_ntuples = PQntuples(fc_stmt->results);

			ecpg_log("ecpg_process_output on line %d: correctly got %d tuples with %d fields\n", fc_stmt->lineno, fc_ntuples, fc_nfields);
			fc_status = true;

			if (fc_ntuples < 1)
			{
				if (fc_ntuples)
					ecpg_log("ecpg_process_output on line %d: incorrect number of matches (%d)\n",
							 fc_stmt->lineno, fc_ntuples);
				ecpg_raise(fc_stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
				fc_status = false;
				break;
			}

			if (fc_var != NULL && fc_var->type == ECPGt_descriptor)
			{
				struct descriptor *fc_desc = ecpg_find_desc(fc_stmt->lineno, fc_var->pointer);

				if (fc_desc == NULL)
					fc_status = false;
				else
				{
					if (fc_desc->result)
						PQclear(fc_desc->result);
					fc_desc->result = fc_stmt->results;
					fc_clear_result = false;
					ecpg_log("ecpg_process_output on line %d: putting result (%d tuples) into descriptor %s\n",
							 fc_stmt->lineno, PQntuples(fc_stmt->results), (const char *) fc_var->pointer);
				}
				fc_var = fc_var->next;
			}
			else if (fc_var != NULL && fc_var->type == ECPGt_sqlda)
			{
				if (INFORMIX_MODE(fc_stmt->compat))
				{
					struct sqlda_compat **fc__sqlda = (struct sqlda_compat **) fc_var->pointer;
					struct sqlda_compat *fc_sqlda = *fc__sqlda;
					struct sqlda_compat *fc_sqlda_new;
					int			fc_i;

					/*
					 * 如果我们传入一个先前存在的sqlda（链）
					 * 那么释放它。
					 */
					while (fc_sqlda)
					{
						fc_sqlda_new = fc_sqlda->desc_next;
						free(fc_sqlda);
						fc_sqlda = fc_sqlda_new;
					}
					*fc__sqlda = fc_sqlda = fc_sqlda_new = NULL;
					for (fc_i = fc_ntuples - 1; fc_i >= 0; fc_i--)
					{
						/*
						 * 构建一个新的sqlda结构。注意只支持
						 * 获取1条记录
						 */
						fc_sqlda_new = ecpg_build_compat_sqlda(fc_stmt->lineno, fc_stmt->results, fc_i, fc_stmt->compat);

						if (!fc_sqlda_new)
						{
							/* 清理我们创建的所有SQLDAs */
							while (fc_sqlda)
							{
								fc_sqlda_new = fc_sqlda->desc_next;
								free(fc_sqlda);
								fc_sqlda = fc_sqlda_new;
							}
							*fc__sqlda = NULL;

							ecpg_log("ecpg_process_output on line %d: out of memory allocating a new sqlda\n", fc_stmt->lineno);
							fc_status = false;
							break;
						}
						else
						{
							ecpg_log("ecpg_process_output on line %d: new sqlda was built\n", fc_stmt->lineno);

							*fc__sqlda = fc_sqlda_new;

							ecpg_set_compat_sqlda(fc_stmt->lineno, fc__sqlda, fc_stmt->results, fc_i, fc_stmt->compat);
							ecpg_log("ecpg_process_output on line %d: putting result (1 tuple %d fields) into sqlda descriptor\n",
									 fc_stmt->lineno, PQnfields(fc_stmt->results));

							fc_sqlda_new->desc_next = fc_sqlda;
							fc_sqlda = fc_sqlda_new;
						}
					}
				}
				else
				{
					struct sqlda_struct **fc__sqlda = (struct sqlda_struct **) fc_var->pointer;
					struct sqlda_struct *fc_sqlda = *fc__sqlda;
					struct sqlda_struct *fc_sqlda_new;
					int			fc_i;

					/*
					 * 如果我们传入一个先前存在的sqlda（链）
					 * 那么释放它。
					 */
					while (fc_sqlda)
					{
						fc_sqlda_new = fc_sqlda->desc_next;
						free(fc_sqlda);
						fc_sqlda = fc_sqlda_new;
					}
					*fc__sqlda = fc_sqlda = fc_sqlda_new = NULL;
					for (fc_i = fc_ntuples - 1; fc_i >= 0; fc_i--)
					{
						/*
						 * 构建一个新的sqlda结构。注意只支持
						 * 获取1条记录
						 */
						fc_sqlda_new = ecpg_build_native_sqlda(fc_stmt->lineno, fc_stmt->results, fc_i, fc_stmt->compat);

						if (!fc_sqlda_new)
						{
							/* 清理我们创建的所有SQLDAs */
							while (fc_sqlda)
							{
								fc_sqlda_new = fc_sqlda->desc_next;
								free(fc_sqlda);
								fc_sqlda = fc_sqlda_new;
							}
							*fc__sqlda = NULL;

							ecpg_log("ecpg_process_output on line %d: out of memory allocating a new sqlda\n", fc_stmt->lineno);
							fc_status = false;
							break;
						}
						else
						{
							ecpg_log("ecpg_process_output on line %d: new sqlda was built\n", fc_stmt->lineno);

							*fc__sqlda = fc_sqlda_new;

							ecpg_set_native_sqlda(fc_stmt->lineno, fc__sqlda, fc_stmt->results, fc_i, fc_stmt->compat);
							ecpg_log("ecpg_process_output on line %d: putting result (1 tuple %d fields) into sqlda descriptor\n",
									 fc_stmt->lineno, PQnfields(fc_stmt->results));

							fc_sqlda_new->desc_next = fc_sqlda;
							fc_sqlda = fc_sqlda_new;
						}
					}
				}

				fc_var = fc_var->next;
			}
			else
				for (fc_act_field = 0; fc_act_field < fc_nfields && fc_status; fc_act_field++)
				{
					if (fc_var != NULL)
					{
						fc_status = ecpg_store_result(fc_stmt->results, fc_act_field, fc_stmt, fc_var);
						fc_var = fc_var->next;
					}
					else if (!INFORMIX_MODE(fc_stmt->compat))
					{
						ecpg_raise(fc_stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL);
						return false;
					}
				}

			if (fc_status && fc_var != NULL)
			{
				ecpg_raise(fc_stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL);
				fc_status = false;
			}

			break;
		case PGRES_COMMAND_OK:
			fc_status = true;
			fc_cmdstat = PQcmdStatus(fc_stmt->results);
			fc_sqlca->sqlerrd[1] = PQoidValue(fc_stmt->results);
			fc_sqlca->sqlerrd[2] = atol(PQcmdTuples(fc_stmt->results));
			ecpg_log("ecpg_process_output on line %d: OK: %s\n", fc_stmt->lineno, fc_cmdstat);
			if (fc_stmt->compat != ECPG_COMPAT_INFORMIX_SE &&
				!fc_sqlca->sqlerrd[2] &&
				(strncmp(fc_cmdstat, "UPDATE", 6) == 0
				 || strncmp(fc_cmdstat, "INSERT", 6) == 0
				 || strncmp(fc_cmdstat, "DELETE", 6) == 0))
				ecpg_raise(fc_stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
			break;
		case PGRES_COPY_OUT:
			{
				char	   *fc_buffer;
				int			fc_res;

				ecpg_log("ecpg_process_output on line %d: COPY OUT data transfer in progress\n", fc_stmt->lineno);
				while ((fc_res = PQgetCopyData(fc_stmt->connection->connection,
											&fc_buffer, 0)) > 0)
				{
					printf("%s", fc_buffer);
					PQfreemem(fc_buffer);
				}
				if (fc_res == -1)
				{
					/* 复制完成 */
					PQclear(fc_stmt->results);
					fc_stmt->results = PQgetResult(fc_stmt->connection->connection);
					if (PQresultStatus(fc_stmt->results) == PGRES_COMMAND_OK)
						ecpg_log("ecpg_process_output on line %d: got PGRES_COMMAND_OK after PGRES_COPY_OUT\n", fc_stmt->lineno);
					else
						ecpg_log("ecpg_process_output on line %d: got error after PGRES_COPY_OUT: %s", fc_stmt->lineno, PQresultErrorMessage(fc_stmt->results));
				}
				break;
			}
		default:

			/*
			 * 执行代码不应该达到这一部分，因为它已在 ecpg_check_PQresult() 中处理
			 */
			ecpg_log("ecpg_process_output on line %d: unknown execution status type\n",
					 fc_stmt->lineno);
			ecpg_raise_backend(fc_stmt->lineno, fc_stmt->results, fc_stmt->connection->connection, fc_stmt->compat);
			fc_status = false;
			break;
	}

	if (fc_clear_result)
	{
		PQclear(fc_stmt->results);
		fc_stmt->results = NULL;
	}

	/* 检查异步返回 */
	PQconsumeInput(fc_stmt->connection->connection);
	while ((fc_notify = PQnotifies(fc_stmt->connection->connection)) != NULL)
	{
		ecpg_log("ecpg_process_output on line %d: asynchronous notification of \"%s\" from backend PID %d received\n",
				 fc_stmt->lineno, fc_notify->relname, fc_notify->be_pid);
		PQfreemem(fc_notify);
		PQconsumeInput(fc_stmt->connection->connection);
	}

	return fc_status;
}

/*
 * ecpg_do_prologue
 *
 * 为执行语句初始化各种基础设施元素：
 *
 *	- 创建语句结构
 *	- 设置与后端通信的 C 数字语言环境
 *	- 将输入/输出参数的变量列表预处理成
 *	  链表
 */
bool ecpg_do_prologue(int fc_lineno, const int fc_compat, const int fc_force_indicator,
				 const char *fc_connection_name, const bool fc_questionmarks,
				 enum ECPG_statement_type fc_statement_type, const char *fc_query,
				 va_list fc_args, struct statement **fc_stmt_out)
{
	struct statement *fc_stmt = NULL;
	struct connection *fc_con;
	enum ECPGttype fc_type;
	struct variable **fc_list;
	char	   *fc_prepname;
	bool		fc_is_prepared_name_set;

	*fc_stmt_out = NULL;

	if (!fc_query)
	{
		ecpg_raise(fc_lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
		return false;
	}

#ifdef ENABLE_THREAD_SAFETY
	ecpg_pthreads_init();
#endif

	fc_con = ecpg_get_connection(fc_connection_name);

	if (!ecpg_init(fc_con, fc_connection_name, fc_lineno))
		return false;

	fc_stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), fc_lineno);

	if (fc_stmt == NULL)
		return false;

	/*
	 * 确保我们不尊重数字输入/输出的语言环境，因为数据库希望使用标准小数点。
	 * 如果可用，请使用 uselocale()，因为它是线程安全的。Windows 没有
	 * 这个，但通常有 _configthreadlocale()。在某些版本的
	 * MinGW 中，_configthreadlocale() 存在但总是返回 -1——因此
	 * 将这种情况视为该函数不存在。
	 */
#ifdef HAVE_USELOCALE

	/*
	 * 由于 ecpg_init() 成功，我们已建立连接。任何成功的
	 * 连接都会初始化 ecpg_clocale。
	 */
	Assert(ecpg_clocale);
	fc_stmt->oldlocale = uselocale(ecpg_clocale);
	if (fc_stmt->oldlocale == (locale_t) 0)
	{
		ecpg_do_epilogue(fc_stmt);
		return false;
	}
#else
#ifdef HAVE__CONFIGTHREADLOCALE
	fc_stmt->oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
#endif
	fc_stmt->oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), fc_lineno);
	if (fc_stmt->oldlocale == NULL)
	{
		ecpg_do_epilogue(fc_stmt);
		return false;
	}
	setlocale(LC_NUMERIC, "C");
#endif

	/*
	 * 如果语句类型为 ECPGst_prepnormal，我们应该在执行它们之前准备语句
	 */
	if (fc_statement_type == ECPGst_prepnormal)
	{
		if (!ecpg_auto_prepare(fc_lineno, fc_connection_name, fc_compat, &fc_prepname, fc_query))
		{
			ecpg_do_epilogue(fc_stmt);
			return false;
		}

		/*
		 * 语句现在已准备好，因此我们必须执行名称而不是查询
		 */
		fc_stmt->command = fc_prepname;
		fc_statement_type = ECPGst_execute;
	}
	else
		fc_stmt->command = ecpg_strdup(fc_query, fc_lineno);

	fc_stmt->name = NULL;

	if (fc_statement_type == ECPGst_execute)
	{
		/* 如果我们有 EXECUTE 命令，则仅发送名称 */
		char	   *fc_command = ecpg_prepared(fc_stmt->command, fc_con);

		if (fc_command)
		{
			fc_stmt->name = fc_stmt->command;
			fc_stmt->command = ecpg_strdup(fc_command, fc_lineno);
		}
		else
		{
			ecpg_raise(fc_lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, fc_stmt->command);
			ecpg_do_epilogue(fc_stmt);
			return false;
		}
	}
	/* PREPARE AS 的名称将在 inlist 的循环中设置 */

	fc_stmt->connection = fc_con;
	fc_stmt->lineno = fc_lineno;
	fc_stmt->compat = fc_compat;
	fc_stmt->force_indicator = fc_force_indicator;
	fc_stmt->questionmarks = fc_questionmarks;
	fc_stmt->statement_type = fc_statement_type;

	/*------
	 * 创建变量列表
	 *
	 * 输入变量在输出变量之前列出。每组的结束由结束标记标记。
	 * 对于每个变量，我们列出：
	 *
	 * 类型 - 在 ecpgtype.h 中定义
	 * 值 - 存储数据的位置
	 * varcharsize - 如果我们有字符串变量的长度，否则为 0
	 * arraysize - 指针为 0（我们不知道数组的大小），简单变量为 1，数组为大小
	 * offset - 数组中第 i 和第 (i+1) 个条目之间的偏移量，通常
	 * 这意味着 sizeof(type)
	 * ind_type - 指示变量的类型
	 * ind_pointer - 指向指示变量的指针
	 * ind_varcharsize - 空
	 * ind_arrsize - 指示数组的数组大小
	 * ind_offset - 指示偏移量
	 *------
	 */

	fc_is_prepared_name_set = false;

	fc_list = &(fc_stmt->inlist);

	fc_type = va_arg(fc_args, enum ECPGttype);

	while (fc_type != ECPGt_EORT)
	{
		if (fc_type == ECPGt_EOIT)
			fc_list = &(fc_stmt->outlist);
		else
		{
			struct variable *fc_var,
					   *fc_ptr;

			if (!(fc_var = (struct variable *) ecpg_alloc(sizeof(struct variable), fc_lineno)))
			{
				ecpg_do_epilogue(fc_stmt);
				return false;
			}

			fc_var->type = fc_type;
			fc_var->pointer = va_arg(fc_args, char *);

			fc_var->varcharsize = va_arg(fc_args, long);
			fc_var->arrsize = va_arg(fc_args, long);
			fc_var->offset = va_arg(fc_args, long);

			/*
			 * 未知的数组大小意味着指向数组的指针。未知
			 * varcharsize 通常也意味着指针。但如果类型是
			 * 字符并且数组大小是已知的，它就是指向 char 的指针数组，因此使用 var->pointer。
			 */
			if (fc_var->arrsize == 0 ||
				(fc_var->varcharsize == 0 && ((fc_var->type != ECPGt_char && fc_var->type != ECPGt_unsigned_char) || (fc_var->arrsize <= 1))))
				fc_var->value = *((char **) (fc_var->pointer));
			else
				fc_var->value = fc_var->pointer;

			/*
			 * 负值用于指示没有给定边界的数组
			 */
			/* 重置为零 */
			if (fc_var->arrsize < 0)
				fc_var->arrsize = 0;
			if (fc_var->varcharsize < 0)
				fc_var->varcharsize = 0;

			fc_var->next = NULL;

			fc_var->ind_type = va_arg(fc_args, enum ECPGttype);
			fc_var->ind_pointer = va_arg(fc_args, char *);
			fc_var->ind_varcharsize = va_arg(fc_args, long);
			fc_var->ind_arrsize = va_arg(fc_args, long);
			fc_var->ind_offset = va_arg(fc_args, long);

			if (fc_var->ind_type != ECPGt_NO_INDICATOR
				&& (fc_var->ind_arrsize == 0 || fc_var->ind_varcharsize == 0))
				fc_var->ind_value = *((char **) (fc_var->ind_pointer));
			else
				fc_var->ind_value = fc_var->ind_pointer;

			/*
			 * 负值用于指示没有给定边界的数组
			 */
			/* 重置为零 */
			if (fc_var->ind_arrsize < 0)
				fc_var->ind_arrsize = 0;
			if (fc_var->ind_varcharsize < 0)
				fc_var->ind_varcharsize = 0;

			/* 如果变量为 NULL，则语句尚未准备 */
			if (fc_var->pointer == NULL)
			{
				ecpg_raise(fc_lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, NULL);
				ecpg_free(fc_var);
				ecpg_do_epilogue(fc_stmt);
				return false;
			}

			for (fc_ptr = *fc_list; fc_ptr && fc_ptr->next; fc_ptr = fc_ptr->next)
				;

			if (fc_ptr == NULL)
				*fc_list = fc_var;
			else
				fc_ptr->next = fc_var;

			if (!fc_is_prepared_name_set && fc_stmt->statement_type == ECPGst_prepare)
			{
				fc_stmt->name = ecpg_strdup(fc_var->value, fc_lineno);
				fc_is_prepared_name_set = true;
			}
		}

		fc_type = va_arg(fc_args, enum ECPGttype);
	}

	/* 我们连接了吗？ */
	if (fc_con == NULL || fc_con->connection == NULL)
	{
		ecpg_raise(fc_lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (fc_con) ? fc_con->name : ecpg_gettext("<empty>"));
		ecpg_do_epilogue(fc_stmt);
		return false;
	}

	if (!fc_is_prepared_name_set && fc_stmt->statement_type == ECPGst_prepare)
	{
		ecpg_raise(fc_lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (fc_con) ? fc_con->name : ecpg_gettext("<empty>"));
		ecpg_do_epilogue(fc_stmt);
		return false;
	}

	/* 初始化 auto_mem 结构 */
	ecpg_clear_auto_mem();

	*fc_stmt_out = fc_stmt;

	return true;
}

/*
 * ecpg_do_epilogue
 *	  恢复应用程序的本地化设置并释放语句结构。
 */
void ecpg_do_epilogue(struct statement *fc_stmt)
{
	if (fc_stmt == NULL)
		return;

#ifdef HAVE_USELOCALE
	if (fc_stmt->oldlocale != (locale_t) 0)
		uselocale(fc_stmt->oldlocale);
#else
	if (fc_stmt->oldlocale)
		setlocale(LC_NUMERIC, fc_stmt->oldlocale);
#ifdef HAVE__CONFIGTHREADLOCALE

	/*
	 * 这比看起来要复杂一点：如果在语句初始化过程中失败，
	 * oldthreadlocale 仍然可能为 0。但这没关系，因为使用 0 的调用被定义为无操作。
	 */
	if (fc_stmt->oldthreadlocale != -1)
		(void) _configthreadlocale(fc_stmt->oldthreadlocale);
#endif
#endif

	fc_free_statement(fc_stmt);
}

/*
 * 在后端执行 SQL 语句。
 * 输入/输出参数（可变参数列表）以 va_list 的形式传递，
 * 这样其他函数可以使用此接口。
 */
bool ecpg_do(const int fc_lineno, const int fc_compat, const int fc_force_indicator, const char *fc_connection_name, const bool fc_questionmarks, const int fc_st, const char *fc_query, va_list fc_args)
{
	struct statement *fc_stmt = NULL;

	if (!ecpg_do_prologue(fc_lineno, fc_compat, fc_force_indicator, fc_connection_name,
						  fc_questionmarks, (enum ECPG_statement_type) fc_st,
						  fc_query, fc_args, &fc_stmt))
		goto fail;

	if (!ecpg_build_params(fc_stmt))
		goto fail;

	if (!ecpg_autostart_transaction(fc_stmt))
		goto fail;

	if (!ecpg_execute(fc_stmt))
		goto fail;

	if (!ecpg_process_output(fc_stmt, true))
		goto fail;

	ecpg_do_epilogue(fc_stmt);
	return true;

fail:
	ecpg_do_epilogue(fc_stmt);
	return false;
}

/*
 * 在后端执行 SQL 语句。
 * 输入/输出参数作为可变长度参数列表传递。
 */
bool ECPGdo(const int fc_lineno, const int fc_compat, const int fc_force_indicator, const char *fc_connection_name, const bool fc_questionmarks, const int fc_st, const char *fc_query,...)
{
	va_list		fc_args;
	bool		fc_ret;

	va_start(fc_args, fc_query);
	fc_ret = ecpg_do(fc_lineno, fc_compat, fc_force_indicator, fc_connection_name,
				  fc_questionmarks, fc_st, fc_query, fc_args);
	va_end(fc_args);

	return fc_ret;
}

/* 旧描述符接口 */
bool ECPGdo_descriptor(int fc_line, const char *fc_connection,
				  const char *fc_descriptor, const char *fc_query)
{
#ifdef FDD //cppcheck
	return ECPGdo(fc_line, ECPG_COMPAT_PGSQL, true, fc_connection, false, 0, fc_query, ECPGt_EOIT,
#else
	return ECPGdo(fc_line, ECPG_COMPAT_PGSQL, true, fc_connection, '\0', 0, fc_query, ECPGt_EOIT,
#endif
				  ECPGt_descriptor, fc_descriptor, 0L, 0L, 0L,
				  ECPGt_NO_INDICATOR, NULL, 0L, 0L, 0L, ECPGt_EORT);
}
